package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.LEARNING_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.SIGN_IN;
import static com.tianji.learning.constants.RedisConstants.SIGN_RECORD_KEY_PREFIX;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;


    /**
     * @param
     * @Description 签到功能接口
     * @Author 老李头儿
     * @Date 2025年04月01 14:30:34
     * @Return com.tianji.learning.domain.vo.SignResultVO
     **/
    @Override
    public SignResultVO sign() {
        //签到(获取签到key  计算offset值 签到并核查是否重复签到)
        String key = getSignKey();
        int offset = LocalDate.now().getDayOfMonth() - 1;
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exists)) {
            throw new BizIllegalException("您已经签到过了，请明天再来");
        }

        //计算连续签到天数
        int signdays = countSignDays();

        //计算签到奖励积分
        int rewardPoints = computerRewardPoints(signdays);

        //封装vo
        if (signdays == 0&&rewardPoints==0){
            throw new BizIllegalException("签到失败");
        }

        SignResultVO vo = SignResultVO.builder()
                .signDays(signdays)
                .signPoints(1)
                .rewardPoints(rewardPoints)
                .build();
        //保存积分
        try {
            mqHelper.send(LEARNING_EXCHANGE,SIGN_IN, SignMessage.of(UserContext.getUser(),vo.totalPoints()));
        } catch (Exception e) {
            log.error("签到发送保存积分明细消息失败", e);
        }

        //返回vo
        return vo;
    }

    /**
    * @Description 查询当月签到记录
    * @Author 老李头儿
    * @Date 2025年04月01 16:25:15
    * @param
    * @Return java.lang.Byte[]
    **/

    @Override
    public Byte[] querySignRecords() {
        // 1、获取签到key
        String key = getSignKey();

        // 2、通过bitfield命令获取本月从第一天到今天为止的所有签到记录
        int dayOfMonth = LocalDate.now().getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (ObjectUtils.isEmpty(result)) {
            return new Byte[]{};
        }
        int signResult = result.get(0).intValue();

        // 3、循环每一天的签到情况，赋值给Byte数组
        Byte[] arr = new Byte[dayOfMonth];
        int pos = dayOfMonth - 1;// pos从该数组的最后一位开始
        while (pos >= 0) {
            arr[pos--] = (byte) (signResult & 1);// 将遍历的每一位赋值给Byte数组:arr[索引角标]=signResult & 1
            signResult >>>= 1;// 右移一位
        }
        return arr;
    }

    /**
     * @param
     * @Description 计算签到奖励积分
     * @Author 老李头儿
     * @Date 2025年04月01 14:56:25
     * @Return int
     **/
    private int computerRewardPoints(int signdays) {
        switch (signdays){
            case 7:
                return 10;
            case 14:
                return 20;
            case 28:
                return 40;
            default:
                return 0;
        }
    }


    /**
     * @param
     * @Description 计算连续签到天数
     * @Author 老李头儿
     * @Date 2025年04月01 14:56:25
     * @Return int
     **/

    private int countSignDays() {
        //通过bitfield命令计算获取从当月第一天到当前日期的天数
        String key = getSignKey();
        int dayOfMonth = LocalDate.now().getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(result)) {
            return 0;
        }
        int signresult = result.get(0).intValue();

        //计算连续签到天数
        int days = 0;
        while ((signresult & 1) == 1) {
            signresult = signresult >>> 1;//右移一位
            days++;//连续签到天数加1
        }


        return days;
    }

    /**
     * @param
     * @Description 组装签到key
     * @Author 老李头儿
     * @Date 2025年04月01 14:43:20
     * @Return java.lang.String
     **/

    private String getSignKey() {
        //获取用户id
        Long userId = UserContext.getUser();

        //获取当前日期
//        String time = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));格式化器每次格式化都要new一个 影响效率
        String date = LocalDate.now().format(DateUtils.DATE_SUFFIX_FORMATTER);

        //组装签到key
        return SIGN_RECORD_KEY_PREFIX + userId + date;


    }


}